package com.bill.pokerbe.service;

import com.bill.pokerbe.config.Const;
import com.bill.pokerbe.model.Card;
import com.bill.pokerbe.model.ScoreAndWinType;
import com.bill.pokerbe.model.Status;
import com.bill.pokerbe.model.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class UserService {
    @Autowired
    private CardService cardService;
    @Autowired
    private CountService countService;
    private List<User> userList;
    private List<List<Card>> allUserCardsList;
    private List<Card> tableCards;

    private List<ScoreAndWinType> userScoreList;
    @Getter
    private int userCount;
    private boolean[] isGaming;
    private int gamingUserCount;
    private int[] userMoneyInRound;
    private boolean[] playSound;
    private int smallBlindIndex;
    private int bigBlindIndex;
    @Getter
    @Setter
    private int masterIndex;
    @Getter
    @Setter
    private int actionUserIndex;
    private int moneyTotal;
    @Getter
    private int moneyRound;
    private int round;
    private List<Integer> winnersIndex;
    private int gameStatus;

    public UserService() {
        initGame();
    }

    public void initGame() {
        userList = new ArrayList<>();
        allUserCardsList = new ArrayList<>();
        tableCards = new ArrayList<>();
        userScoreList = new ArrayList<>();
        winnersIndex = new ArrayList<>();
        isGaming = new boolean[10];
        userMoneyInRound = new int[10];
        playSound = new boolean[10];
        smallBlindIndex = 0;
        bigBlindIndex = 1;
        masterIndex = 1;
        gameStatus = Const.GameNotStartStatus;
    }

    public void startGame() {
        userCount = userList.size();
        gamingUserCount = userCount;
        Arrays.fill(isGaming, true);
        Arrays.fill(userMoneyInRound, 0);
        Arrays.fill(playSound, false);
        smallBlindIndex = nextUserIndex(smallBlindIndex);
        bigBlindIndex = nextUserIndex(bigBlindIndex);
        actionUserIndex = nextUserIndex(bigBlindIndex);
        playSound[actionUserIndex] = true;
        if (userCount == 2) {
            masterIndex = smallBlindIndex;
        } else {
            masterIndex = nextUserIndex(bigBlindIndex);
        }
        moneyTotal = 0;
        moneyRound = 0;
        round = 0;
        userScoreList = new ArrayList<>();
        winnersIndex = new ArrayList<>();
        cardService.generateNewCardList();
        tableCards = new ArrayList<>();
        dealCards();

        userList.get(smallBlindIndex).setMoney(userList.get(smallBlindIndex).getMoney() - 10);
        userList.get(bigBlindIndex).setMoney(userList.get(bigBlindIndex).getMoney() - 20);
        userMoneyInRound[smallBlindIndex] = 10;
        userMoneyInRound[bigBlindIndex] = 20;
        moneyTotal = 30;
        moneyRound = 20;

        gameStatus = Const.GameStartStatus;
    }

    private void dealCards() {
        allUserCardsList = new ArrayList<>();
        for (int i = 0; i < userCount; i++) {
            List<Card> cardPair = new ArrayList<>();
            cardPair.add(cardService.getACard());
            cardPair.add(cardService.getACard());
            allUserCardsList.add(cardPair);
        }

        for (int i = 0; i < 5; i++) {
            tableCards.add(cardService.getACard());
        }
    }

    public int getUserIndexByUserId(String userId) {
        for (int i = 0; i < userList.size(); i++) {
            if (Objects.equals(userList.get(i).getId(), userId)) {
                return i;
            }
        }
        return -1;
    }

    public String userStopGame(int userIndex) {
        if (!isGaming[userIndex]) {
            return "已经弃牌了";
        }
        isGaming[userIndex] = false;
        gamingUserCount--;
        if (gamingUserCount == 1) {
            userWinTheGame(null);
            gameStatus = Const.GameEndStatus;
            return "";
        }
        if (masterIndex == userIndex) {
            return Const.WanDianYiTiZhuRen;
        }
        return "";
    }

    public int nextUserIndex(int userIndex) {
        if (userList.isEmpty()) {
            return 0;
        }
        do {
            userIndex++;
            userIndex %= userList.size();
        } while (!isGaming[userIndex]);
        return userIndex;
    }

    public boolean nextIsMaster(int userIndex) {
        return nextUserIndex(userIndex) == masterIndex;
    }

    public void actionUserMoveNext() {
        actionUserIndex = nextUserIndex(actionUserIndex);
        playSound[actionUserIndex] = true;
    }

    public void setNewMaster(int userIndex) {
        masterIndex = userIndex;
    }

    public String userBetting(int userIndex, int money) {
        User user = userList.get(userIndex);
        if (money < 0) {
            return "下注不能为负数";
        }
        if (userMoneyInRound[userIndex] + money < moneyRound) {
            return "不能下注后少于本轮注";
        }
        if (user.getMoney() < money) {
            return "玩家没这么多钱了";
        }
        if (money > 2000) {
            return "最大下注2000";
        }
        user.setMoney(user.getMoney() - money);
        userMoneyInRound[userIndex] += money;
        moneyTotal += money;
        moneyRound = Math.max(moneyRound, userMoneyInRound[userIndex]);
        return "";
    }

    public boolean isUserEqualsGenZhu(int userIndex, int addMoney) {
        return userMoneyInRound[userIndex] + addMoney == moneyRound;
    }

    public void nextRound() {
        moneyRound = 0;
        Arrays.fill(userMoneyInRound, 0);
        round++;
        if (round == 4) {//开牌
            chooseWinner();
        }
    }

    private void chooseWinner() {
        List<UserWithScoreAndWinType> userWithScoreAndWinTypes = new ArrayList<>();
        for (int i = 0; i < userCount; i++) {
            if (isGaming[i]) {
                List<Card> userCardList = allUserCardsList.get(i);
                List<Card> totalList = new ArrayList<>(userCardList);
                totalList.addAll(tableCards);
                ScoreAndWinType userScoreAndWinType = countService.getCardScore(totalList);
                userWithScoreAndWinTypes.add(new UserWithScoreAndWinType(userList.get(i), userScoreAndWinType));
                userScoreList.add(userScoreAndWinType);
            } else {
                userScoreList.add(new ScoreAndWinType(0L, "无"));
            }
        }
        userWithScoreAndWinTypes.sort((o1, o2) -> Long.compare(o2.scoreAndWinType.getScore(), o1.scoreAndWinType.getScore()));

        List<Integer> winnersIndexList = new ArrayList<>();
        for (UserWithScoreAndWinType userWithScoreAndWinType : userWithScoreAndWinTypes) {
            if (userWithScoreAndWinType.scoreAndWinType.getScore() == userWithScoreAndWinTypes.get(0).scoreAndWinType.getScore()) {
                winnersIndexList.add(getUserIndexByUserId(userWithScoreAndWinType.getUser().getId()));
            } else {
                break;
            }
        }

        userWinTheGame(winnersIndexList);
        gameStatus = Const.GameEndStatus;
    }

    public void userWinTheGame(List<Integer> usersIndex) {
        if (gameStatus != Const.GameStartStatus) {
            return;
        }
        if (usersIndex == null) {
            winnersIndex.add(getFirstGamingUserIndex());
        } else {
            winnersIndex.addAll(usersIndex);
        }

        int moneyEveryone = moneyTotal / winnersIndex.size();
        for (int winnerIndex : winnersIndex) {
            User winner = userList.get(winnerIndex);
            winner.setMoney(winner.getMoney() + moneyEveryone);
            userList.set(winnerIndex, winner);
        }
    }

    private int getFirstGamingUserIndex() {
        for (int i = 0; i < userCount; i++) {
            if (isGaming[i]) {
                return i;
            }
        }
        return -1;
    }

    public Status getStatus(String userId, String username) {
        int userIndex = getUserIndexByUserId(userId);
        if (userIndex == -1) {
            userList.add(new User(userId, username, 10000));
            userIndex = userList.size() - 1;
        }

        Status status = new Status();
        status.setUser(userList);
        status.setMyCards(allUserCardsList == null ? null :
                (allUserCardsList.size() > userIndex ? allUserCardsList.get(userIndex) : null));
        status.setOpenCards(getOpenCards());
        status.setIsGaming(isGaming);
        status.setIsGaming(isGaming);
        status.setUserMoneyInRound(userMoneyInRound);
        status.setSmallBlindIndex(smallBlindIndex);
        status.setBigBlindIndex(bigBlindIndex);
        status.setActionUserIndex(actionUserIndex);
        status.setMoneyTotal(moneyTotal);
        status.setMoneyRound(moneyRound);
        status.setRound(round);
        status.setMasterIndex(masterIndex);
        status.setWinnersIndex(winnersIndex);
        status.setGameStatus(gameStatus);
        if (gameStatus != Const.GameEndStatus) {
            status.setAllUserCards(new ArrayList<>());
        } else {
            status.setAllUserCards(allUserCardsList);
        }
        status.setUserScoreList(userScoreList);
        if (playSound[userIndex]) {
            playSound[userIndex] = false;
            status.setPlaySound(true);
        } else {
            status.setPlaySound(false);
        }
        return status;
    }

    public List<Card> getOpenCards() {
        List<Card> openCards = new ArrayList<>();
        int cardNum = 0;
        if (round == 1) {
            cardNum = 3;
        } else if (round == 2) {
            cardNum = 4;
        } else if (round >= 3) {
            cardNum = 5;
        }
        for (int i = 0; i < cardNum; i++) {
            if (tableCards.size() <= i) {
                break;
            }
            openCards.add(tableCards.get(i));
        }
        return openCards;
    }

    public int getUserMoneyInRoundByUserIndex(int userIndex) {
        return userMoneyInRound[userIndex];
    }

    public boolean isGameStart() {
        return gameStatus == Const.GameStartStatus;
    }

    public boolean isUserGaming(int userIndex) {
        return gameStatus == Const.GameStartStatus && isGaming[userIndex];
    }

    public String addMoney(String userId, int amount) {
        int userIndex = getUserIndexByUserId(userId);
        if (userIndex == -1) {
            return "用户不存在";
        }
        User user = userList.get(userIndex);
        user.setMoney(user.getMoney() + amount);
        userList.set(userIndex,user);
        return "";
    }
}

@Data
@AllArgsConstructor
class UserWithScoreAndWinType {
    User user;
    ScoreAndWinType scoreAndWinType;
}